#pragma once

template<int v>
struct Int
{
        enum {c_value=v};
        typedef Int<v> value;
};

template<bool b>
struct Bool
{
    enum {c_value=b};
    typedef Bool<b> value;
};

#define binaryOp(name,op, RetType) template<int x, int y>\
struct _binary##name\
{\
    enum {c_value = x op y };\
};\
struct name\
{\
struct _\
 {\
 typedef _ value;\
        template<class x>\
        struct app\
        {\
          struct _\
            {\
            typedef _ value;\
                template<class y>\
                struct app\
                {\
                 struct _:  RetType< _binary##name<x::c_value,y::c_value>::c_value >\
                    {\
                        typedef RetType< _binary##name<x::c_value,y::c_value>::c_value > value;\
                    };\
            };\
        };\
    };\
 };\
}

#define unaryOp(name,op,RetType) template<int x>\
struct _unary##name\
{\
    enum {c_value = op x };\
};\
struct name\
{\
struct _\
 {\
 typedef _ value;\
    template<class x>\
    struct app\
    {\
        struct _:  RetType< _unary##name<x::c_value>::c_value >\
        {\
        };\
    };\
  };\
}

binaryOp(pAdd,+,Int);
binaryOp(pMinus,-,Int);
binaryOp(pDiv,/,Int);
binaryOp(pPlus,*,Int);
binaryOp(pMod,%,Int);
binaryOp(pGreat,>,Bool);
binaryOp(pLess,<,Bool);
binaryOp(pGreatEq,>=,Bool);
binaryOp(pLessEq,<=,Bool);
binaryOp(pEq,==,Bool);
binaryOp(pOr,||,Int);
binaryOp(pAnd,&&,Int);
binaryOp(pNotEq,!=,Bool);
unaryOp(pNeg,-,Int);
unaryOp(pNot,!,Bool);


struct Error_NOExpresion
{

};

/*if*/
template<class v, class isok=Error_NOExpresion, class notok=Error_NOExpresion>
struct IF:IF<typename v::value, isok, notok>
{
};

template<class isok, class notok>
struct IF<Bool<true>, isok, notok>
{
    struct _:isok
    {
        typedef typename isok::value value;
    };
};

template<class isok, class notok>
struct IF<Bool<false>, isok, notok>
{
    struct _:notok
    {
        typedef typename notok::value value;
    };
};


template<typename R, bool>
class ASSERT
{
    ASSERT();
    ASSERT(char*);
    operator R();
};

template<typename R>
class ASSERT<R, true>
{
    public:
    ASSERT(){}
    ASSERT(char*){}
    operator R();
};



#define app(x) _::app<x >
#define evaluate _::value::c_value

/*switch*/


